/**
 * @file ConnectionPool.h
 * @brief 数据库连接池的实现
 * 
 * 本文件实现了一个线程安全的数据库连接池，支持：
 * - 多种数据库类型（SQLite、MySQL）
 * - 连接的自动管理和复用
 * - 连接池大小的动态调整
 * - 空闲连接的超时处理
 * - 事务的处理
 * 
 * @author 数据库库开发团队
 * @version 1.0
 * @date 2023-07-15
 */

#pragma once

#include <Poco/Data/SessionPool.h>
#include <Poco/Data/Session.h>
#include <memory>
#include <string>
#include <mutex>
#include <queue>
#include <chrono>

#include "../CLink_Export.h"
#include "DatabaseError.h"

namespace CLink 
{
namespace Db 
{

/**
 * @enum DatabaseType
 * @brief 支持的数据库类型枚举
 * 
 * 定义了连接池支持的数据库类型。目前支持SQLite和MySQL，
 * 后续可以扩展支持其他数据库类型。
 */
enum class DatabaseType {
    SQLITE,     ///< SQLite数据库
    MYSQL       ///< MySQL数据库
};

/**
 * @struct ConnectionConfig
 * @brief 数据库连接配置结构体
 * 
 * 包含了建立数据库连接所需的所有配置参数，包括：
 * - 基本连接信息（类型、主机、端口等）
 * - 数据库认证信息
 * - SQLite特定配置
 * - 连接池参数
 */
struct CLINK_CLASS ConnectionConfig {
    DatabaseType type;       ///< 数据库类型
    std::string host;        ///< 数据库主机地址
    uint16_t port;          ///< 数据库端口号
    std::string database;    ///< 数据库名称
    std::string username;    ///< 用户名
    std::string password;    ///< 密码
    std::string charset = "utf8";  ///< 字符集编码
    
    /**
     * @brief SQLite特定配置
     * @{
     */
    bool inMemory = false;   ///< 是否使用内存数据库，仅用于SQLite
    /** @} */
    
    /**
     * @brief 连接池配置参数
     * @{
     */
    size_t minSessions = 1;   ///< 连接池保持的最小连接数
    size_t maxSessions = 32;  ///< 连接池允许的最大连接数
    size_t idleTime = 60;     ///< 空闲连接的超时时间（秒）
    /** @} */
    
    /**
     * @brief 根据配置创建数据库连接字符串
     * 
     * 根据数据库类型和配置参数，生成相应的连接字符串：
     * - 对于SQLite：返回":memory:"或数据库文件路径
     * - 对于MySQL：生成包含主机、端口、数据库名、用户名、密码和字符集的连接字符串
     * 
     * @return 格式化的数据库连接字符串
     */
    std::string createConnectionString() const {
        if (type == DatabaseType::SQLITE) {
            return inMemory ? ":memory:" : database;
        }
        
        // MySQL连接字符串
        std::string connStr = "host=" + host;
        connStr += ";port=" + std::to_string(port);
        connStr += ";db=" + database;
        connStr += ";user=" + username;
        connStr += ";password=" + password;
        connStr += ";charset=" + charset;
        return connStr;
    }
};

/**
 * @class Connection
 * @brief 数据库连接的包装器类
 * 
 * 封装了Poco::Data::Session，提供了更高级的接口，包括：
 * - 事务管理
 * - 连接状态检查
 * - 会话访问控制
 * 
 * 使用示例：
 * @code
 * auto conn = pool.getConnection();
 * try {
 *     conn->begin();
 *     // 执行数据库操作
 *     conn->commit();
 * } catch (const std::exception& e) {
 *     conn->rollback();
 *     throw;
 * }
 * @endcode
 */
class CLINK_CLASS Connection {
public:
    /**
     * @brief 构造函数
     * @param session Poco数据库会话对象
     */
    Connection(Poco::Data::Session session) : _session(session) {}
    
    /**
     * @brief 获取底层数据库会话的引用
     * @return 数据库会话引用
     */
    Poco::Data::Session& getSession() { return _session; }

    /**
     * @brief 获取底层数据库会话的常量引用
     * @return 数据库会话常量引用
     */
    const Poco::Data::Session& getSession() const { return _session; }
    
    /**
     * @brief 开始一个新的事务
     * @throw TransactionException 如果无法开始事务
     */
    void begin() {
        _session.begin();
    }
    
    /**
     * @brief 提交当前事务
     * @throw TransactionException 如果事务提交失败
     */
    void commit() {
        _session.commit();
    }
    
    /**
     * @brief 回滚当前事务
     * @throw TransactionException 如果事务回滚失败
     */
    void rollback() {
        _session.rollback();
    }
    
    /**
     * @brief 检查连接是否有效
     * @return true 如果连接正常，false 如果连接已断开或无效
     */
    bool isValid() {
        return _session.isConnected();
    }

private:
    Poco::Data::Session _session;  ///< 底层数据库会话对象
};

/**
 * @class ConnectionPool
 * @brief 线程安全的数据库连接池
 * 
 * 提供高效的数据库连接管理，特性包括：
 * - 自动创建和回收数据库连接
 * - 连接的有效性验证
 * - 支持连接超时设置
 * - 线程安全的连接获取和释放
 * - 连接池状态监控
 * 
 * 使用示例：
 * @code
 * // 创建连接池配置
 * ConnectionConfig config;
 * config.type = DatabaseType::MYSQL;
 * config.host = "localhost";
 * config.port = 3306;
 * config.database = "testdb";
 * config.username = "user";
 * config.password = "pass";
 * 
 * // 初始化连接池
 * ConnectionPool pool(config);
 * 
 * // 获取连接并使用
 * try {
 *     auto conn = pool.getConnection();
 *     // 使用连接执行数据库操作
 *     // ...
 *     // 连接会在shared_ptr析构时自动返回池中，也可以显式调用
 *     pool.releaseConnection(conn);
 * } catch (const DatabaseException& e) {
 *     std::cerr << "数据库错误: " << e.what() << std::endl;
 * }
 * @endcode
 */
class CLINK_CLASS ConnectionPool {
public:
    /**
     * @typedef ConnectionPtr
     * @brief 连接对象的智能指针类型
     */
    using ConnectionPtr = std::shared_ptr<Connection>;
    
    /**
     * @brief 构造函数
     * 
     * @param config 连接池配置
     * @throw ConnectionException 如果初始化连接池失败
     */
    ConnectionPool(const ConnectionConfig& config);
    
    /**
     * @brief 析构函数
     * 
     * 关闭连接池并释放所有资源
     */
    ~ConnectionPool();
    
    /**
     * @brief 获取数据库连接
     * 
     * 从连接池中获取一个可用的数据库连接。如果没有可用连接且未达到最大连接数，
     * 会创建新的连接。如果达到最大连接数，将等待直到有连接可用或超时。
     * 
     * @param timeout 获取连接的超时时间，默认为5秒
     * @return 数据库连接的智能指针
     * @throw PoolException 如果无法获取连接或连接池已关闭
     * @throw ConnectionException 如果创建新连接失败
     */
    ConnectionPtr getConnection(std::chrono::milliseconds timeout = std::chrono::milliseconds(5000));
    
    /**
     * @brief 释放连接回连接池
     * 
     * 通常不需要直接调用此方法，因为连接会在shared_ptr析构时自动返回池中。
     * 
     * @param connection 要释放的数据库连接
     * @throw PoolException 如果连接池已关闭
     */
    void releaseConnection(ConnectionPtr connection);
    
    /**
     * @brief 获取当前活跃连接数
     * @return 当前正在使用的连接数量
     */
    size_t getActiveConnections() const;
    
    /**
     * @brief 获取当前空闲连接数
     * @return 当前可用的空闲连接数量
     */
    size_t getIdleConnections() const;
    
    /**
     * @brief 关闭连接池
     * 
     * 关闭所有连接并释放资源。调用此方法后，连接池将不再可用。
     */
    void shutdown();

private:
    ConnectionConfig _config;                              ///< 连接池配置信息
    std::unique_ptr<Poco::Data::SessionPool> _pool;       ///< POCO数据库会话池
    mutable std::mutex _mutex;                            ///< 保护连接池操作的互斥锁
    bool _shutdown;                                       ///< 连接池关闭标志
    
    /**
     * @brief 创建新的数据库连接
     * 
     * 根据配置信息创建一个新的数据库连接。这个方法会在以下情况被调用：
     * - 连接池初始化时创建初始连接
     * - 当没有空闲连接且未达到最大连接数时
     * - 当检测到无效连接需要替换时
     * 
     * @return 新创建的数据库连接
     * @throw ConnectionException 如果创建连接失败
     */
    ConnectionPtr createConnection();
    
    /**
     * @brief 验证连接的有效性
     * 
     * 通过执行一个轻量级的测试查询来验证连接是否仍然可用。
     * 这个方法在以下情况下被调用：
     * - 从连接池获取连接时
     * - 连接被释放回池中时
     * - 定期检查空闲连接时
     * 
     * @param conn 要验证的连接
     * @return true 如果连接有效，false 如果连接已失效
     */
    bool validateConnection(Connection& conn);
};
} // namespace db
} // namespace CLink